package com.zeratul.common.lock.lock.redisson;

import com.zeratul.common.core.context.DataContextHolde;
import com.zeratul.common.core.utils.spring.SpringUtils;
import com.zeratul.common.lock.annotation.ZtLockType;
import com.zeratul.common.lock.constants.LockConstants;
import com.zeratul.common.lock.lock.base.BaseZtLock;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 基于Redssion的分布式锁方案
 */
@Slf4j
public class RedissonZtLock extends BaseZtLock {

    private static RedissonClient redissonClient;

    /**
     * 获取RedissonClient对象
     * @return
     */
    public static RedissonClient getRedissonClient() {
        if (redissonClient != null) {
            return redissonClient;
        }

        //双重锁判定 保证获取RedissonClient的线程安全
        synchronized (RedissonZtLock.class) {
            if (redissonClient != null) {
                return redissonClient;
            }
            redissonClient = SpringUtils.getBean(RedissonClient.class);
            log.debug("【分布式锁】初始化，获得Redssion对象：{}", redissonClient);
            return redissonClient;
        }
    }

    /**
     * 将分布式锁对象加入到ThreadLocal中
     * @param rLock
     */
    public static void addRLock(RLock rLock){
        List<RLock> rLocks = DataContextHolde.getReqData(LockConstants.ZT_LOCK_CONTEXT_KEY);
        if (rLocks == null) {
            rLocks = new ArrayList<>();
            DataContextHolde.setReqData(LockConstants.ZT_LOCK_CONTEXT_KEY, rLocks);
        }
        rLocks.add(rLock);
    }

    /**
     * 从ThreadLocal中获取分布式锁对象
     */
    public static RLock getRLock(){
        List<RLock> rLocks = DataContextHolde.getReqData(LockConstants.ZT_LOCK_CONTEXT_KEY);
        if (CollectionUtils.isEmpty(rLocks)) {
            DataContextHolde.removeReqData(LockConstants.ZT_LOCK_CONTEXT_KEY);
            return null;
        }
        RLock rlock = rLocks.remove(rLocks.size() - 1);
        if (CollectionUtils.isEmpty(rLocks)) {
            DataContextHolde.removeReqData(LockConstants.ZT_LOCK_CONTEXT_KEY);
        }
        return rlock;
    }

    /**
     * 根据锁类型获取锁
     * @param ztLockType
     * @param key
     * @return
     */
    public static RLock getRLock(ZtLockType ztLockType, String key){
        switch (ztLockType) {
            case FAIR_LOCK:
                return getRedissonClient().getFairLock(key);
            case REENTRANT_LOCK:
            default:
                return getRedissonClient().getLock(key);
        }
    }

    /**
     * 阻塞式加锁
     *
     * @param ztLockType
     * @param key
     */
    @Override
    public void lock(ZtLockType ztLockType, String key) {
        RLock lock = getRLock(ztLockType, key);
        //加锁
        lock.lock();
        log.debug("【分布式锁】获取【阻塞式】分布式锁成功，key：{}", lock.getName());
        //将分布式锁加入到ThreadLock中
        addRLock(lock);
    }

    /**
     * 非阻塞式加锁
     *
     * @param ztLockType
     * @param key
     * @param timeout
     */
    @Override
    public boolean tryLock(ZtLockType ztLockType, String key, Integer timeout) {
        RLock lock = getRLock(ztLockType, key);
        try {
            if (lock.tryLock(timeout, TimeUnit.MILLISECONDS)) {
                log.debug("【分布式锁】获取【非阻塞式】分布式锁成功，key：{}", lock.getName());
                //将分布式锁加入到ThreadLock中
                addRLock(lock);
                return true;
            }
        } catch (Exception e) {}
        log.warn("【分布式锁】获取【非阻塞式】分布式锁失败，key：{}", lock.getName());
        return false;
    }

    /**
     * 解锁
     */
    @Override
    public void unlock() {
        RLock rLock = getRLock();
        if (rLock == null || !rLock.isLocked()) {
            return;
        }
        log.debug("【分布式锁】解除分布式锁成功，key：{}", rLock.getName());
        rLock.unlock();
    }
}
